package com.hz.tgb.data.mongo;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.mongodb.BasicDBObject;
import com.mongodb.client.*;
import com.mongodb.client.model.IndexOptions;
import com.mongodb.client.model.InsertManyOptions;
import com.mongodb.client.result.UpdateResult;
import lombok.extern.slf4j.Slf4j;
import org.bson.Document;
import org.bson.conversions.Bson;
import org.bson.json.JsonWriterSettings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * 使用mongo-java-driver-3.10.2连接MongoDB数据库
 *
 * @author hezhao created on 2019/12/5 8:59
 */
@Component
@Slf4j
public class MongoUnDBUtil {

    @Autowired
    private MongoClient mongoClient;


    /**
     * 获取DB实例 - 指定DB
     *
     * @param
     * @return
     */
    private MongoDatabase getDB(String db) {
        return mongoClient.getDatabase(db);
    }

    /**
     * 添加索引
     *
     * @param t
     * @param <T>
     * @return TODO
     * <p>
     * <p>
     * Indexes.ascending("key");
     * Indexes.descending("key"); //正序、倒序
     * Indexes.text(""); //全文检索索引
     * Indexes.hashed("hash索引");
     * mongoDBUtil.createIndex(new SysArea(),Indexes.text("key"));
     * <p>
     * <p>
     * BasicDBObject ind=new BasicDBObject();//联合索引
     * ind.append("kye",-1).append("key2",1);
     * mongoDBUtil.createIndex(new SysArea(),ind);
     * <p>
     * BasicDBObject ind2=new BasicDBObject();//单个索引
     * ind.append("kye",-1);
     * mongoDBUtil.createIndex(new SysArea(),ind2);
     * <p>
     * IndexOptions indexOptions = new IndexOptions();
     * indexOptions.unique(true);// 唯一约束
     * mongoDBUtil.createIndex(new SysArea(),ind2,indexOptions);
     */
    public <T> String createIndex(String db, T t, Bson keys, IndexOptions indexOptions) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        String retKey = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).createIndex(keys, indexOptions);
        return retKey;
    }

    /**
     * 添加索引
     *
     * @param t
     * @param <T>
     * @return TODO
     * <p>
     * <p>
     * Indexes.ascending("key");
     * Indexes.descending("key"); //正序、倒序
     * Indexes.text(""); //全文检索索引
     * Indexes.hashed("hash索引");
     * mongoDBUtil.createIndex(new SysArea(),Indexes.text("key"));
     * <p>
     * <p>
     * BasicDBObject ind=new BasicDBObject();//联合索引
     * ind.append("kye",-1).append("key2",1);
     * mongoDBUtil.createIndex(new SysArea(),ind);
     * <p>
     * BasicDBObject ind2=new BasicDBObject();//单个索引
     * ind.append("kye",-1);
     * mongoDBUtil.createIndex(new SysArea(),ind2);
     * <p>
     * IndexOptions indexOptions = new IndexOptions();
     * indexOptions.unique(true);// 唯一约束
     * mongoDBUtil.createIndex(new SysArea(),ind2,indexOptions);
     */
    public <T> String createIndex(String db, T t, Bson keys) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        String retKey = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).createIndex(keys);
        return retKey;
    }


    /**
     * 添加数据
     *
     * @param t
     * @param <T>
     * @return
     */
    public <T> T addData(String db, T t) {

        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document document = JSONObject.parseObject(JSON.toJSONString(t), Document.class);
        getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).insertOne(document);
        return t;
    }

    public <T> T addData(String db,String collection, T t) {

        if(StringUtils.isEmpty(collection)){
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document document = JSONObject.parseObject(JSON.toJSONString(t), Document.class);
        getDB(db).getCollection(collection.toLowerCase()).insertOne(document);
        return t;
    }

    /**
     * 添加数据
     *
     * @param list
     * @param <T>
     * @return
     */
    public <T> List<T> addData(String db, List<T> list) {
        if (list.isEmpty()) {
            throw new NullPointerException("对象不能为空");
        }
        try {
            List<Document> documents = JSONObject.parseArray(JSON.toJSONString(list), Document.class);
            getDB(db).getCollection(list.get(0).getClass().getSimpleName().toLowerCase()).insertMany(documents, new InsertManyOptions().ordered(false));
            return list;
        } catch (Exception e) {
            throw new NullPointerException("插入失败");
        }

    }

    public <T> List<T> addData(String db,String collection, List<T> list) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (list.isEmpty()) {
            throw new NullPointerException("对象不能为空");
        }
        try {
            List<Document> documents = JSONObject.parseArray(JSON.toJSONString(list), Document.class);
            getDB(db).getCollection(collection.toLowerCase()).insertMany(documents, new InsertManyOptions().ordered(false));
            return list;
        } catch (Exception e) {
            throw new NullPointerException("插入失败");
        }

    }


    /**
     * 更新
     *
     * @param t
     * @param <T>
     * @return TODO  BasicDBObject query=new BasicDBObject();//
     * Bson filter = new BasicDBObject("$gte", 12).append("$lte",30);
     * query.put("userId",filter);// 用户id 大于等于12，小于等于30
     */
    public <T> Boolean updateMany(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document modifiers = new Document();
        Document document = JSONObject.parseObject(JSON.toJSONString(t), Document.class);
        modifiers.append("$set", document);
        Boolean ret = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).updateMany(filter, modifiers).wasAcknowledged();
        return ret;
    }


    public <T> Boolean updateMany(String db,String collection, Bson filter, T t) {

        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document modifiers = new Document();
        Document document = JSONObject.parseObject(JSON.toJSONString(t), Document.class);
        modifiers.append("$set", document);
        Boolean ret = getDB(db).getCollection(collection.toLowerCase()).updateMany(filter, modifiers).wasAcknowledged();
        return ret;
    }

    /**
     * 更新
     *
     * @param t
     * @param <T>
     * @return TODO  BasicDBObject query=new BasicDBObject();//
     * Bson filter = new BasicDBObject("$gte", 12).append("$lte",30);
     * query.put("userId",filter);// 用户id 大于等于12，小于等于30
     */
    public <T> Boolean updateOne(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document modifiers = new Document();
        Document document = JSONObject.parseObject(JSON.toJSONString(t), Document.class);
        modifiers.append("$set", document);
        Boolean ret = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).updateOne(filter, modifiers).wasAcknowledged();
        return ret;
    }

    public <T> Boolean updateOne(String db,String collection, Bson filter, T t) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document modifiers = new Document();
        Document document = JSONObject.parseObject(JSON.toJSONString(t), Document.class);
        modifiers.append("$set", document);
        Boolean ret = getDB(db).getCollection(collection.toLowerCase()).updateOne(filter, modifiers).wasAcknowledged();
        return ret;
    }


    /**
     * 删除单个
     *
     * @param filter
     * @param t
     * @param <T>
     * @return
     */
    public <T> boolean delOne(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        return getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).deleteOne(filter).wasAcknowledged();
    }

    public <T> boolean delOne(String db,String collection, Bson filter) {

        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        return getDB(db).getCollection(collection.toLowerCase()).deleteOne(filter).wasAcknowledged();
    }


    /**
     * 删除多个
     *
     * @param filter
     * @param t
     * @param <T>
     * @return
     */
    public <T> boolean deleteMany(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        return getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).deleteMany(filter).wasAcknowledged();
    }

    public <T> boolean deleteMany(String db,String collection, Bson filter) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }

        return getDB(db).getCollection(collection.toLowerCase()).deleteMany(filter).wasAcknowledged();
    }


    /**
     * 分组聚合统计
     *
     * @param t
     * @param <T>
     * @return
     */
    public <T> List<T> aggregate(String db, String collection, List<? extends Bson> pipeline, T t) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        AggregateIterable<Document> iterable = getDB(db).getCollection(collection).aggregate(pipeline).allowDiskUse(true);
        return aggregateIterableToList(iterable, t);
    }

    /**
     * 分组聚合统计
     *
     * @param t
     * @param <T>
     * @return
     */
    public <T> List<T> aggregate(String db, List<? extends Bson> pipeline, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        AggregateIterable<Document> iterable = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).aggregate(pipeline).allowDiskUse(true);

        return aggregateIterableToList(iterable, t);
    }

    public <T> T toBean(BasicDBObject dbObject, Class<T> clzss) {
        String realJson = dbObject.toJson(JsonWriterSettings.builder().build());
        T obj = JSON.parseObject(realJson, clzss);
        return obj;
    }

    public <T> T toBean(Document document, Class<T> clzss) {
        String realJson = document.toJson(JsonWriterSettings.builder().build());
        T obj = JSON.parseObject(realJson, clzss);
        return obj;
    }

    private <T> List<T> aggregateIterableToList(AggregateIterable<Document> aggregateIterable, T t) {
        List<T> list = new ArrayList<>();
        MongoCursor<Document> iterator = aggregateIterable.iterator();
        while (iterator.hasNext()) {
            Document document = iterator.next();
            list.add((T) toBean(document, t.getClass()));
        }
        return list;
    }


    /**
     * 查询全部
     *
     * @param filter
     * @param t
     * @param <T>
     * @return TODO  BasicDBObject query=new BasicDBObject();//
     * Bson filter = new BasicDBObject("$gte", 12).append("$lte",30);
     * query.put("userId",filter);  // 用户id 大于等于12，小于等于30
     */
    public <T> List<T> find(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        FindIterable<Document> iterable = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).find(filter);
        return findIterableToList(iterable, t);
    }

    public <T> List<T> find(String db, String collection, Bson filter, T t) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        FindIterable<Document> iterable = getDB(db).getCollection(collection.toLowerCase()).find(filter);
        return findIterableToList(iterable, t);
    }

    /**
     * @param filter
     * @param t
     * @param sort
     * @param <T>
     * @return TODO  BasicDBObject query=new BasicDBObject();//
     * Bson filter = new BasicDBObject("$gte", 12).append("$lte",30);
     * query.put("userId",filter);  // 用户id 大于等于12，小于等于30
     * <p>
     * BasicDBObject sort = new BasicDBObject();
     * sort.put("age",-1);  // -1倒序 ，1正序
     */
    public <T> List<T> find(String db, Bson filter, T t, BasicDBObject sort) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        FindIterable<Document> iterable = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).find(filter).sort(sort);
        return findIterableToList(iterable, t);

    }

    public <T> List<T> find(String db, String collection, Bson filter, T t, BasicDBObject sort) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        FindIterable<Document> iterable = getDB(db).getCollection(collection.toLowerCase()).find(filter).sort(sort);
        return findIterableToList(iterable, t);

    }


    public <T> List<T> find(String db, Bson filter, T t, BasicDBObject sort, int limit) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        if (limit <= 0) {
            throw new NullPointerException("limit 非法");
        }
        FindIterable<Document> iterable = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).find(filter).sort(sort).limit(limit);
        return findIterableToList(iterable, t);
    }

    public <T> List<T> find(String db, String collection, Bson filter, T t, BasicDBObject sort, int limit) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        if (limit <= 0) {
            throw new NullPointerException("limit 非法");
        }
        FindIterable<Document> iterable = getDB(db).getCollection(collection.toLowerCase()).find(filter).sort(sort).limit(limit);
        return findIterableToList(iterable, t);
    }


    public <T> Long findAndUpdate(String db, Bson filter, Bson update, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        UpdateResult updateResult = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).updateMany(filter, update);

        return updateResult.getMatchedCount();

    }

    public <T> Long findAndUpdate(String db, String collection, Bson filter, Bson update) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
      
        UpdateResult updateResult = getDB(db).getCollection(collection.toLowerCase()).updateMany(filter, update);

        return updateResult.getMatchedCount();

    }

    /**
     * 查询单个
     *
     * @param filter
     * @param t
     * @param <T>
     * @return
     */
    public <T> T findByOne(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document doc = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).find(filter).first();
        return docToEntity(doc, t);

    }

    public <T> T findByOne(String db, String collection, Bson filter, T t) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        Document doc = getDB(db).getCollection(collection.toLowerCase()).find(filter).first();
        return docToEntity(doc, t);

    }

    private <T> T docToEntity(Document doc, T t) {
        if (doc == null) {
            return null;
        }
        doc.remove("_id");
        String s = JSONObject.toJSONString(doc, new JSONValueFilter());
        T ret = (T) JSON.parseObject(s, t.getClass());
        return ret;

    }

    /***
     * 返回mongo 文档对象
     */
    public Document findOneDocument(String db, Bson filter, Class clazz) {
        Document doc = getDB(db).getCollection(clazz.getSimpleName().toLowerCase()).find(filter).first();
        if (doc == null) {
            return null;
        }
        doc.remove("_id");
        log.info("document json:{}", JSON.toJSONString(doc));
        return doc;
    }



    private <T> List<T> findIterableToList(FindIterable<Document> iterable, T t) {
        List<T> list = new ArrayList<>();
        for (Document doc : iterable) {
            doc.remove("_id");
            String s = JSONObject.toJSONString(doc, new JSONValueFilter());
            T ret = (T) JSON.parseObject(s, t.getClass());
            list.add(ret);
        }
        return list;

    }


    /**
     * 分页查询
     *
     * @param filter
     * @param t
     * @param page
     * @param rows
     * @param sort
     * @param <T>
     * @return TODO  BasicDBObject query=new BasicDBObject();//
     * Bson filter = new BasicDBObject("$gte", 12).append("$lte",30);
     * query.put("userId",filter);// 用户id 大于等于12，小于等于30
     */
    public <T> List<T> findByPage(String db, Bson filter, T t, int page, int rows, BasicDBObject sort) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        if (page <= 1) {
            page = 0;
        }
        if (rows < 1 || rows > 100) {
            rows = 100;
        }
        if(sort == null){
            FindIterable<Document> iterable = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).find(filter).skip((page - 1) * rows).limit(rows);
            return findIterableToList(iterable, t);
        }
        FindIterable<Document> iterable = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).find(filter).skip((page - 1) * rows).limit(rows).sort(sort);
        return findIterableToList(iterable, t);


    }

    public <T> List<T> findByPage(String db, String collection,Bson filter, T t, int page, int rows, BasicDBObject sort) {
        if (StringUtils.isEmpty(collection)) {
            throw new NullPointerException("collection不能为空");
        }
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        if (page <= 1) {
            page = 0;
        }
        if (rows < 1 || rows > 100) {
            rows = 100;
        }
        if(sort == null){
            FindIterable<Document> iterable = getDB(db).getCollection(collection.toLowerCase()).find(filter).skip((page - 1) * rows).limit(rows);
            return findIterableToList(iterable, t);
        }
        FindIterable<Document> iterable = getDB(db).getCollection(collection.toLowerCase()).find(filter).skip((page - 1) * rows).limit(rows).sort(sort);
        return findIterableToList(iterable, t);

    }


    /**
     * 文档数量
     *
     * @param filter
     * @param t
     * @param <T>
     * @return
     */
    public <T> long count(String db, Bson filter, T t) {
        if (t == null) {
            throw new NullPointerException("对象不能为空");
        }
        long cnt = getDB(db).getCollection(t.getClass().getSimpleName().toLowerCase()).countDocuments(filter);
        return cnt;
    }



    /**
     * @param cllection 指定文档名称
     * @param filter
     * @return
     */
    public long count(String db, String cllection, Bson filter) {
        if (cllection == null) {
            throw new NullPointerException("对象不能为空");
        }
        long cnt = getDB(db).getCollection(cllection).countDocuments(filter);
        return cnt;
    }

    /**
     * 初始化mongodb查询条件对象
     *
     * @param map
     * @return
     */
    public BasicDBObject getBasicDBObject(Map<String, Object> map) {
        if (CollectionUtils.isEmpty(map)) {
            return null;
        }
        BasicDBObject query = new BasicDBObject();
        for (Map.Entry<String, Object> entry : map.entrySet()) {
            query.put(entry.getKey(), entry.getValue());
        }
        return query;
    }
}